package Helper;

import android.accounts.NetworkErrorException;
import android.content.Context;
import android.support.annotation.NonNull;

import com.example.administrator.drinkdevice.R;

import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
import org.xml.sax.InputSource;
import org.xml.sax.SAXParseException;

import Annotation.*;

import java.io.BufferedReader;
import java.io.ByteArrayInputStream;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.net.ConnectException;
import java.net.HttpURLConnection;
import java.net.SocketTimeoutException;
import java.net.URL;
import java.util.List;
import java.util.concurrent.TimeoutException;

import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;

import Const.PublicDefine;
import Entity.BaseEnity;
import Interface.IAsynCallBackListener;
import Interface.IHttp;

/**
 * Created by 王彦鹏 on 2017-09-02.
 */

class HttpAsynResponseData {
    private String response = "";
    private Throwable exception;

    public String getResponse() {
        return response;
    }

    public void setResponse(String response) {
        this.response = response;
    }

    public Throwable getException() {
        return exception;
    }

    public void setException(Throwable exception) {
        this.exception = exception;
    }
}

public class Http implements IHttp {

    private Context ctx;

    public Http(Context context) {
        this.ctx = context;
    }

    /**
     * 用Get方式请求服务器
     *
     * @param url         url 地址
     * @param contentType 请求数据类型
     * @return 服务器返回的数据
     */
    @NonNull
    private String httpMethod(String url, String contentType, String method, String postData) throws Exception {
        HttpURLConnection conn = null;
        long t1, t2 = 0;
        t1 = System.currentTimeMillis();
        Log.write("Http", String.format("%s 请求(%s)\r\nUrl：%s\r\n请求数据：%s", method, contentType, url, postData));
        try {


            URL murl = new URL(url);

            //1.得到HttpURLConnection实例化对象
            conn = (HttpURLConnection) murl.openConnection();
            //2.设置请求信息（请求方式... ...）
            //设置请求方式和响应时间
            conn.setRequestMethod(method);
            conn.setRequestProperty("Content-Type", contentType);
            conn.setConnectTimeout(5000);
            conn.setReadTimeout(5000);

            if (method.toUpperCase() == "POST") {
                conn.setRequestProperty("Content-Length", String.valueOf(postData.getBytes().length));
                conn.setRequestProperty("encoding", "UTF-8"); //可以指定编码
                //不使用缓存
                conn.setUseCaches(false);
                // 设置可取
                conn.setDoInput(true);
                // 设置可读
                conn.setDoOutput(true);
                //4.向服务器写入数据
                conn.getOutputStream().write(postData.getBytes());
            }
            //3.读取响应
            if (conn.getResponseCode() == 200) {
                InputStream in = conn.getInputStream();
                // 创建高效流对象
                BufferedReader reader = new BufferedReader(new InputStreamReader(in));
                // 创建StringBuilder对象存储数据
                StringBuilder response = new StringBuilder();
                String line;// 一次读取一行
                while ((line = reader.readLine()) != null) {
                    response.append(line);// 得到的数据存入StringBuilder
                }
                t2 = System.currentTimeMillis();
                Log.write("Http", String.format("%s 请求(%s)\r\nUrl：%s\r\n请求数据：%s\r\n返回数据：%s\r\n请求用时：%3f",
                        method, contentType, url, postData, response.toString(), (t2 - t1) / Double.valueOf(1000)));
                return response.toString();
            } else if (conn.getResponseCode() == 404) {
                t2 = System.currentTimeMillis();
                Log.write("Http", String.format("%s 请求(%s)：%s\r\n请求失败(404)(%3f)。", method, contentType, url, (t2 - t1) / Double.valueOf(1000)));
                throw new Exception(ctx.getResources().getString(R.string.Http_ConnectError404));
            } else {
                t2 = System.currentTimeMillis();
                Log.write("Http", String.format("%s 请求(%s)：%s\r\n请求失败(%d)(%3f)。", method, contentType, url, conn.getResponseCode(), (t2 - t1) / Double.valueOf(1000)));
                throw new Exception(String.format("请求失败(%d)", conn.getResponseCode()));
            }
        } catch (TimeoutException e) {
            t2 = System.currentTimeMillis();
            Log.write("Http", String.format("%s 请求(%s)：%s\r\n连接超时(%3f)。", method, contentType, url, (t2 - t1) / Double.valueOf(1000)));
            throw new Exception(ctx.getResources().getString(R.string.Http_TimeOut));
        } catch (SocketTimeoutException e) {
            t2 = System.currentTimeMillis();
            Log.write("Http", String.format("%s 请求(%s)\r\nUrl：%s\r\n请求超时(%3f)", method, contentType, url, (t2 - t1) / Double.valueOf(1000)));
            throw new Exception(ctx.getResources().getString(R.string.Http_TimeOut));
        } catch (NetworkErrorException e) {
            throw new Exception(ctx.getResources().getString(R.string.Http_NetWorkDisconnect));
        } catch (ConnectException e) {
            throw new Exception(ctx.getResources().getString(R.string.Http_NetWorkDisconnect));
        } catch (Exception e) {
            t2 = System.currentTimeMillis();
            Log.write("Http", String.format("%s 请求(%s)：%s\r\n请求异常:%s", method, contentType, url, e.getMessage()));
            e.printStackTrace();
            if (e.getMessage().indexOf("Network is unreachable") >= 0) {
                throw new Exception(ctx.getResources().getString(R.string.Http_NetWorkDisconnect));
            } else {
                throw e;
            }
        } finally {
            //4.释放资源
            if (conn != null) {
                //关闭连接 即设置 http.keepAlive = false;
                conn.disconnect();
            }
        }
    }

    /**
     * 用Get方式请求服务器
     *
     * @param url         url 地址
     * @param callback    异步回调地址，当不为null时为异步调用
     * @param contentType 请求数据类型
     * @return 服务器返回的数据
     */
    public String Get(final String url,
                      final String contentType,
                      final IAsynCallBackListener callback) throws Exception {
        if (callback == null) {
            return httpMethod(url, contentType, "GET", "");
        } else {
            new Thread(new Runnable() {
                @Override
                public void run() {
                    try {
                        String httpResponse = httpMethod(url, contentType, "GET", "");
                        // 回调onFinish()方法
                        callback.onFinish("", httpResponse);

                    } catch (Exception e) {
                        // 回调onError()方法
                        callback.onError(this, e);
                    }
                }
            }).start();
            return "";
        }

    }


    /**
     * 用Get方式请求服务器
     *
     * @param url 网络地址
     * @return
     */
    public String Get(final String url) throws Exception {
        if (!PublicDefine.NetworkConnState) {
            throw new Exception(ctx.getResources().getString(R.string.Http_NetWorkDisconnectPleaseCheck));
        }
        final Object synObj = new Object();
        final HttpAsynResponseData res = new HttpAsynResponseData();
        Thread getthread = new Thread(new Runnable() {
            @Override
            public void run() {
                synchronized (synObj) {
                    try {
                        res.setResponse(Get(url, "application/x-www-form-urlencoded", null));
                        res.setException(null);
                    } catch (Exception e) {
                        e.printStackTrace();
                        res.setException(e);
                    }
                    synObj.notify();
                }
            }
        });
        getthread.start();
        try {
            //Thread.sleep(1);
            synchronized (synObj) {
                synObj.wait();
                if (res.getException() == null) {
                    return res.getResponse();
                } else {
                    throw new Exception(res.getException());
                }
            }
            //return res[0];
        } catch (Exception e) {
            throw new Exception(e);
        }
    }


    /**
     * 用 Post 方式请求服务器
     *
     * @param url         网络地址
     * @param postData    提交数据
     * @param callback    回调地址
     * @param contentType 请求数据类型
     * @return 服务器返回数据
     */
    public String Post(final String url,
                       final String postData,
                       final IAsynCallBackListener callback,
                       final String contentType) throws Exception {
        if (callback == null) {
            return httpMethod(url, contentType, "POST", postData);
        } else {
            new Thread(new Runnable() {
                @Override
                public void run() {
                    try {
                        String httpResponse = httpMethod(url, contentType, "POST", postData);
                        // 回调onFinish()方法
                        callback.onFinish("", httpResponse);
                    } catch (Exception e) {
                        // 回调onError()方法
                        callback.onError(this, e);
                    }
                }
            }).start();
            return "";
        }
    }


    /**
     * 用 Post 方式请求服务器
     *
     * @param url      网络地址
     * @param postData 提交数据
     * @return
     */
    public String Post(final String url,
                       final String postData) throws Exception {
        if (!PublicDefine.NetworkConnState) {
            throw new Exception(ctx.getResources().getString(R.string.Http_NetWorkDisconnectPleaseCheck));
        }
        final Object synObj = new Object();
        final String[] res = {""};
        Thread getthread = new Thread(new Runnable() {
            @Override
            public void run() {
                synchronized (synObj) {
                    try {
                        res[0] = Post(url, postData, null, "application/x-www-form-urlencoded");
                    } catch (Exception e) {
                        e.printStackTrace();
                        res[0] = e.getMessage();
                    }

                    synObj.notify();
                }
            }
        });
        getthread.start();

        try {
            //Thread.sleep(1);
            synchronized (synObj) {
                synObj.wait();
                return res[0];
            }
            //return res[0];
        } catch (Exception e) {
            throw new Exception(res[0]);
        }

    }

    @Override
    public void Post(String url, String postData, IAsynCallBackListener callback) throws Exception {
        if (!PublicDefine.NetworkConnState) {
            throw new Exception(ctx.getResources().getString(R.string.Http_NetWorkDisconnectPleaseCheck));
        }
        Post(url, postData, callback, "application/x-www-form-urlencoded");

    }

    /**
     * 用 Post 方式请求服务器
     *
     * @param url      网络地址
     * @param postData 提交数据
     * @return
     */
    public String Post(final String url,
                       final String postData,
                       final String contentType) throws Exception {
        if (!PublicDefine.NetworkConnState) {
            throw new Exception(ctx.getResources().getString(R.string.Http_NetWorkDisconnectPleaseCheck));
        }
        final Object synObj = new Object();
        final String[] res = {""};
        Thread getthread = new Thread(new Runnable() {
            @Override
            public void run() {
                synchronized (synObj) {
                    try {
                        res[0] = Post(url, postData, null, contentType);
                    } catch (Exception e) {
                        e.printStackTrace();
                        res[0] = e.getMessage();
                    }

                    synObj.notify();
                }
            }
        });
        getthread.start();

        try {
            //Thread.sleep(1);
            synchronized (synObj) {
                synObj.wait();
                return res[0];
            }
            //return res[0];
        } catch (Exception e) {
            return res[0];
        }
    }

    private static JSONObject LoadNodeChild(Node node) throws JSONException {
        JSONObject json = new JSONObject();
        for (int i = 0; i < node.getChildNodes().getLength(); i++) {
            Node subnode = node.getChildNodes().item(i);
            String key = subnode.getNodeName();
            Object value = null;
            if (subnode.hasChildNodes() && (!subnode.getChildNodes().item(0).getNodeName().equals("#text"))) {
                value = LoadNodeChild(subnode);
            } else {
                value = subnode.getTextContent();
            }
            if (json.has(key)) {
                Object oldJson = json.get(key);
                if (oldJson instanceof JSONObject) {
                    JSONArray jsonArray = new JSONArray();
                    jsonArray.put(oldJson);
                    jsonArray.put(value);
                    json.remove(key);
                    json.put(key, jsonArray);
                } else {
                    ((JSONArray) oldJson).put(value);
                    json.put(key, oldJson);
                }
            } else {
                json.put(key, value);
            }
        }
        return json;
    }

    private static String GetPropertyName(Field f) {
        PropertyName pname = f.getAnnotation(PropertyName.class);
        if (pname == null) {
            return f.getName();
        } else {
            return pname.value();
        }
    }

    private static String GetKeyValue(BaseEnity enity) throws Exception {
        String values = "";
        Field[] field = enity.getClass().getDeclaredFields(); // 获取实体类的所有属性，返回Field数组

        for (int j = 0; j < field.length; j++) { // 遍历所有属性
            String name = field[j].getName(); // 获取属性的名字
            name = name.substring(0, 1).toUpperCase() + name.substring(1); // 将属性的首字符大写，方便构造get，set方法

            try {
                Method m = enity.getClass().getMethod("get" + name);
                Object instance = m.invoke(enity);
                if (instance.getClass().getSimpleName().equals("ArrayList")) {
                    for (int i = 0; i < ((List) instance).size(); i++) {
                        if (((List) instance).get(i).getClass().getSuperclass().getSimpleName().equals("BaseEnity")) {
                            values += "<" + GetPropertyName(field[j]) + ">" + GetKeyValue((BaseEnity) ((List) instance).get(i)) + "</" + GetPropertyName(field[j]) + ">\n";
                        } else {
                            values += "<" + GetPropertyName(field[j]) + ">" + ((List) instance).get(i).toString() + "</" + GetPropertyName(field[j]) + ">\n";
                        }
                    }
                } else if (instance.getClass().getSuperclass().getSimpleName().equals("BaseEnity")) {
                    values += "<" + GetPropertyName(field[j]) + ">" + GetKeyValue((BaseEnity) instance) + "</" + GetPropertyName(field[j]) + ">\n";
                } else {
                    values += "<" + GetPropertyName(field[j]) + ">" + instance.toString() + "</" + GetPropertyName(field[j]) + ">\n";
                }
            } catch (Exception e) {

            }
        }
        return values;
    }

    /**
     * 调用WebService接口
     *
     * @param IName
     * @param args
     * @return
     */
    public JSONObject CallWebService(String url, String IName, BaseEnity... args)
            throws Exception {
        if (!PublicDefine.NetworkConnState) {
            throw new Exception(ctx.getResources().getString(R.string.Http_NetWorkDisconnectPleaseCheck));
        }
        String pdata = "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n" +
                "<soap:Envelope xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" xmlns:xsd=\"http://www.w3.org/2001/XMLSchema\" xmlns:soap=\"http://schemas.xmlsoap.org/soap/envelope/\">\n" +
                /*"<soap:Envelope xmlns:s=\"http://schemas.xmlsoap.org/soap/envelope/\""+*/
                "  <soap:Body xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" xmlns:xsd=\"http://www.w3.org/2001/XMLSchema\" >\n" +
                "<%s xmlns=\"http://tempuri.org/\">\n";
        if (args != null) {
            for (BaseEnity entity : args) {
                pdata += GetKeyValue(entity);
            }

        }
        pdata += "</%s>\n" +
                "  </soap:Body>\n" +
                "</soap:Envelope>";
        pdata = String.format(pdata, IName, IName);

        String resxml = Post(url, pdata, "text/xml");
        DocumentBuilderFactory xml = DocumentBuilderFactory.newInstance();
        DocumentBuilder builder = xml.newDocumentBuilder();
        try {
            Document dom = builder.parse(new InputSource(new ByteArrayInputStream(resxml.getBytes("utf-8"))));

            Element root = dom.getDocumentElement();
            NodeList items = root.getElementsByTagName(IName + "Response");//查找所有person节点
            Node response = items.item(0);
            JSONObject resJson = LoadNodeChild(response);
            return resJson;
        } catch (SAXParseException e) {
            e.printStackTrace();
            throw new Exception(resxml);
        }
    }

    @Override
    public String Get(String url, IAsynCallBackListener callback) throws Exception {
        return Get(url, "application/x-www-form-urlencoded", callback);
    }

    @Override
    public void CallWebService(String url, final String IName, final IAsynCallBackListener callback, BaseEnity... args) throws Exception {
        if (!PublicDefine.NetworkConnState) {
            throw new Exception(ctx.getResources().getString(R.string.Http_NetWorkDisconnectPleaseCheck));
        }
        String pdata = "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n" +
                "<soap:Envelope xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" xmlns:xsd=\"http://www.w3.org/2001/XMLSchema\" xmlns:soap=\"http://schemas.xmlsoap.org/soap/envelope/\">\n" +
                /*"<soap:Envelope xmlns:s=\"http://schemas.xmlsoap.org/soap/envelope/\""+*/
                "  <soap:Body xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" xmlns:xsd=\"http://www.w3.org/2001/XMLSchema\" >\n" +
                "<%s xmlns=\"http://tempuri.org/\">\n";
        if (args != null) {
            for (BaseEnity entity : args) {
                pdata += GetKeyValue(entity);
            }

        }
        pdata += "</%s>\n" +
                "  </soap:Body>\n" +
                "</soap:Envelope>";
        pdata = String.format(pdata, IName, IName);

        Post(url, pdata, new IAsynCallBackListener() {
            @Override
            public void onFinish(Object sender, Object data) {
                try {
                    String resxml = data.toString();
                    DocumentBuilderFactory xml = DocumentBuilderFactory.newInstance();
                    DocumentBuilder builder = xml.newDocumentBuilder();
                    Document dom = builder.parse(new InputSource(new ByteArrayInputStream(resxml.getBytes("utf-8"))));

                    Element root = dom.getDocumentElement();
                    NodeList items = root.getElementsByTagName(IName + "Response");//查找所有person节点
                    Node response = items.item(0);
                    JSONObject resJson = LoadNodeChild(response);
                    callback.onFinish("", resJson);
                } catch (Exception e) {

                }
            }

            @Override
            public void onError(Object sender, Exception e) {
                callback.onError(this, e);
            }
        }, "text/xml");

    }
}
